/**
 * \file kernel/driver/pci/pci.c
 * \author Davide Gessa
 * \date 10-25-09
 */
#include "pci.h"
#include "pci_list.h"
#include <arch.h>
#include <klibc/ctype.h>
#include <klibc/string.h>
#include <debug.h>
#include <driver.h>


/**
 * Legge il risultato di un commando allo slot pci
 */
uint32_t pci_config_wread(uint16_t bus, uint16_t slot, uint16_t func, uint16_t offset)
{   
	io_outd(0xCF8, (uint32_t) (bus << 16) |
     (uint32_t) (slot << 11) |
     (uint32_t) (func << 8) |
     (uint32_t) (offset & /*~*/3) |
     (uint32_t) 0x80000000);
	

	return (uint32_t)((io_ind(0xCFC) >> ((offset & 3) * 8)) & 0xFF);
}


/**
 * Restituisce il pci_device nella corrispondenza slot bus
 */
uint32_t pci_get(uint8_t slot, uint8_t bus, device_t *pci_dev)
{
	uint32_t x;
	uint32_t device = pci_config_wread(bus, slot, 0, 2);
	uint32_t vendor = pci_config_wread(bus, slot, 0, 0);
	uint32_t classcode = pci_config_wread(bus, slot, 8, 2);
			
	if(vendor != 0xFF && device != 0xFF  && classcode != 0xFF)
	{
		pci_dev->bus = bus;
		pci_dev->slot = slot;
		pci_dev->device_id = device;
		
		/*
		for(x = 0; x < PCI_DEVTABLE_LEN; x++)
		{
			if(PciDevTable[x].DevId == device)// && PciDevTable[x].VenId == vendor)
			{
				strcpy(pci_dev->device, PciDevTable[x].Chip);
				strcpy(pci_dev->device_desc, PciDevTable[x].ChipDesc);
				break;
			}
					
		}
		if(vendor != 0xFF)
		{
			pci_dev->vendor_id = vendor;
			
			for(x = 0; x < PCI_VENTABLE_LEN; x++)
				if(vendor == PciVenTable[x].VenId)
				{
					break;
				}
		}
		*/			

		pci_dev->class_id = classcode;
			
		for(x = 0; x < PCI_CLASS_CODE_TABLE_LEN; x++)
			if(classcode == (pci_class_code_table[x].class_base + (pci_class_code_table[x].class_sub >> 8)) )
			{
				strcpy(pci_dev->class, pci_class_code_table[x].desc_base);
				strcpy(pci_dev->class_desc, pci_class_code_table[x].desc_sub);
				
				if(strcmp(pci_class_code_table[x].file, "\0") == 0) break;
				
				strcpy(pci_dev->file, pci_class_code_table[x].file);
				break;
			}
		
		return 1;
	}	
	else
		return 0;
}


/**
 * Aggiunge i device pci alla lista
 */
void pci_update(device_t **list)
{
	int slot;
	int bus;
	device_t *dev;
			
			
	for(bus = 0; bus < 256; bus++)
	{
		for(slot = 0; slot < 32; slot++)
		{
			dev = (device_t *) malloc(sizeof(device_t));
			if(pci_get(slot, bus, dev))
			{
				dev->next = *list;
				*list = dev;
			}
			else
				free(dev);
		}
	}	
}
